Istražite Reactov useActionState hook za pojednostavljeno upravljanje stanjem pokrenuto asinkronim akcijama. Poboljšajte učinkovitost i korisničko iskustvo vaše aplikacije.
Implementacija React useActionState: Upravljanje stanjem temeljeno na akcijama
Reactov useActionState hook, predstavljen u novijim verzijama, nudi profinjen pristup upravljanju ažuriranjima stanja koja proizlaze iz asinkronih akcija. Ovaj moćan alat pojednostavljuje proces obrade mutacija, ažuriranja korisničkog sučelja i upravljanja stanjima grešaka, posebno pri radu s React Server Components (RSC) i poslužiteljskim akcijama. Ovaj vodič istražit će zamršenosti useActionState, pružajući praktične primjere i najbolje prakse za implementaciju.
Razumijevanje potrebe za upravljanjem stanjem temeljenim na akcijama
Tradicionalno upravljanje stanjem u Reactu često uključuje odvojeno upravljanje stanjima učitavanja i grešaka unutar komponenata. Kada akcija (npr. slanje obrasca, dohvaćanje podataka) pokrene ažuriranje stanja, programeri obično upravljaju tim stanjima s više useState poziva i potencijalno složenom uvjetnom logikom. useActionState pruža čišće i integriranije rješenje.
Razmotrimo jednostavan scenarij slanja obrasca. Bez useActionState, mogli biste imati:
- Varijablu stanja za podatke obrasca.
- Varijablu stanja za praćenje je li obrazac u procesu slanja (stanje učitavanja).
- Varijablu stanja za čuvanje poruka o greškama.
Ovaj pristup može dovesti do opširnog koda i potencijalnih nedosljednosti. useActionState objedinjuje ove probleme u jedan hook, pojednostavljujući logiku i poboljšavajući čitljivost koda.
Predstavljanje useActionState
useActionState hook prihvaća dva argumenta:
- Asinkronu funkciju ("akciju") koja izvršava ažuriranje stanja. To može biti poslužiteljska akcija ili bilo koja asinkrona funkcija.
- Početnu vrijednost stanja.
Vraća polje koje sadrži dva elementa:
- Trenutnu vrijednost stanja.
- Funkciju za pokretanje akcije. Ova funkcija automatski upravlja stanjima učitavanja i grešaka povezanima s akcijom.
Evo osnovnog primjera:
import { useActionState } from 'react';
async function updateServer(prevState, formData) {
// Simulate an asynchronous server update.
await new Promise(resolve => setTimeout(resolve, 1000));
const data = Object.fromEntries(formData);
if (data.name === "error") {
return 'Failed to update server.';
}
return `Updated name to: ${data.name}`;
}
function MyComponent() {
const [state, dispatch] = useActionState(updateServer, 'Initial State');
async function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
const result = await dispatch(formData);
console.log(result);
}
return (
);
}
U ovom primjeru:
updateServerje asinkrona akcija koja simulira ažuriranje poslužitelja. Prima prethodno stanje i podatke obrasca.useActionStateinicijalizira stanje s 'Initial State' i vraća trenutno stanje tedispatchfunkciju.handleSubmitfunkcija pozivadispatchs podacima obrasca.useActionStateautomatski upravlja stanjima učitavanja i grešaka tijekom izvršavanja akcije.
Obrada stanja učitavanja i grešaka
Jedna od ključnih prednosti useActionState je ugrađeno upravljanje stanjima učitavanja i grešaka. dispatch funkcija vraća promise koji se razrješava s rezultatom akcije. Ako akcija baci grešku, promise se odbija s tom greškom. To možete iskoristiti za odgovarajuće ažuriranje korisničkog sučelja.
Izmijenimo prethodni primjer kako bismo prikazali poruku o učitavanju i poruku o grešci:
import { useActionState } from 'react';
import { useState } from 'react';
async function updateServer(prevState, formData) {
// Simulate an asynchronous server update.
await new Promise(resolve => setTimeout(resolve, 1000));
const data = Object.fromEntries(formData);
if (data.name === "error") {
throw new Error('Failed to update server.');
}
return `Updated name to: ${data.name}`;
}
function MyComponent() {
const [state, dispatch] = useActionState(updateServer, 'Initial State');
const [isSubmitting, setIsSubmitting] = useState(false);
const [errorMessage, setErrorMessage] = useState(null);
async function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
setIsSubmitting(true);
setErrorMessage(null);
try {
const result = await dispatch(formData);
console.log(result);
} catch (error) {
console.error("Error during submission:", error);
setErrorMessage(error.message);
} finally {
setIsSubmitting(false);
}
}
return (
);
}
Ključne promjene:
- Dodali smo
isSubmittingierrorMessagevarijable stanja za praćenje stanja učitavanja i grešaka. - U
handleSubmit, postavljamoisSubmittingnatrueprije pozivadispatchi hvatamo sve greške kako bismo ažuriralierrorMessage. - Onemogućujemo gumb za slanje dok je u tijeku slanje i uvjetno prikazujemo poruke o učitavanju i greškama.
useActionState s poslužiteljskim akcijama u React Server Components (RSC)
useActionState se posebno ističe kada se koristi s React Server Components (RSC) i poslužiteljskim akcijama. Poslužiteljske akcije su funkcije koje se izvršavaju na poslužitelju i mogu izravno mijenjati izvore podataka. Omogućuju vam izvođenje operacija na strani poslužitelja bez pisanja API krajnjih točaka.
Napomena: Ovaj primjer zahtijeva React okruženje konfigurirano za Server Components i Server Actions.
// app/actions.js (Server Action)
'use server';
import { cookies } from 'next/headers'; //Example, for Next.js
export async function updateName(prevState, formData) {
const name = formData.get('name');
if (!name) {
return 'Please enter a name.';
}
try {
// Simulate database update.
await new Promise(resolve => setTimeout(resolve, 1000));
cookies().set('userName', name);
return `Updated name to: ${name}`; //Success!
} catch (error) {
console.error("Database update failed:", error);
return 'Failed to update name.'; // Important: Return a message, not throw an Error
}
}
// app/page.jsx (React Server Component)
'use client';
import { useActionState } from 'react';
import { updateName } from './actions';
function MyComponent() {
const [state, dispatch] = useActionState(updateName, 'Initial State');
async function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
const result = await dispatch(formData);
console.log(result);
}
return (
);
}
export default MyComponent;
U ovom primjeru:
updateNameje poslužiteljska akcija definirana uapp/actions.js. Prima prethodno stanje i podatke obrasca, ažurira bazu podataka (simulirano) i vraća poruku o uspjehu ili grešci. Ključno je da akcija vraća poruku umjesto bacanja greške. Poslužiteljske akcije preferiraju vraćanje informativnih poruka.- Komponenta je označena kao klijentska komponenta (
'use client') kako bi se mogao koristitiuseActionStatehook. handleSubmitfunkcija pozivadispatchs podacima obrasca.useActionStateautomatski upravlja ažuriranjem stanja na temelju rezultata poslužiteljske akcije.
Važna razmatranja za poslužiteljske akcije
- Obrada grešaka u poslužiteljskim akcijama: Umjesto bacanja grešaka, vratite smislenu poruku o grešci iz vaše poslužiteljske akcije.
useActionStateće tretirati ovu poruku kao novo stanje. To omogućuje elegantnu obradu grešaka na klijentu. - Optimistična ažuriranja: Poslužiteljske akcije mogu se koristiti s optimističnim ažuriranjima za poboljšanje percipiranih performansi. Možete odmah ažurirati korisničko sučelje i vratiti ga u prethodno stanje ako akcija ne uspije.
- Ponovna validacija: Nakon uspješne mutacije, razmislite o ponovnoj validaciji keširanih podataka kako biste osigurali da korisničko sučelje odražava najnovije stanje.
Napredne tehnike s useActionState
1. Korištenje reducera za složena ažuriranja stanja
Za složeniju logiku stanja, možete kombinirati useActionState s reducer funkcijom. To vam omogućuje upravljanje ažuriranjima stanja na predvidljiv i održiv način.
import { useActionState } from 'react';
import { useReducer } from 'react';
const initialState = {
count: 0,
message: 'Initial State',
};
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'DECREMENT':
return { ...state, count: state.count - 1 };
case 'SET_MESSAGE':
return { ...state, message: action.payload };
default:
return state;
}
}
async function updateState(state, action) {
// Simulate asynchronous operation.
await new Promise(resolve => setTimeout(resolve, 500));
switch (action.type) {
case 'INCREMENT':
return reducer(state, action);
case 'DECREMENT':
return reducer(state, action);
case 'SET_MESSAGE':
return reducer(state, action);
default:
return state;
}
}
function MyComponent() {
const [state, dispatch] = useActionState(updateState, initialState);
return (
Count: {state.count}
Message: {state.message}
);
}
2. Optimistična ažuriranja s useActionState
Optimistična ažuriranja poboljšavaju korisničko iskustvo trenutnim ažuriranjem korisničkog sučelja kao da je akcija bila uspješna, a zatim vraćanjem ažuriranja ako akcija ne uspije. To može učiniti vašu aplikaciju responzivnijom.
import { useActionState } from 'react';
import { useState } from 'react';
async function updateServer(prevState, formData) {
// Simulate an asynchronous server update.
await new Promise(resolve => setTimeout(resolve, 1000));
const data = Object.fromEntries(formData);
if (data.name === "error") {
throw new Error('Failed to update server.');
}
return `Updated name to: ${data.name}`;
}
function MyComponent() {
const [name, setName] = useState('Initial Name');
const [state, dispatch] = useActionState(async (prevName, newName) => {
try {
const result = await updateServer(prevName, {
name: newName,
});
return newName; // Update on success
} catch (error) {
// Revert on error
console.error("Update failed:", error);
setName(prevName);
return prevName;
}
}, name);
async function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
const newName = formData.get('name');
setName(newName); // Optimistically update UI
await dispatch(newName);
}
return (
);
}
3. Debouncing akcija
U nekim scenarijima, možda ćete htjeti koristiti "debounce" za akcije kako biste spriječili njihovo prečesto pokretanje. To može biti korisno za scenarije poput polja za pretraživanje gdje želite pokrenuti akciju tek nakon što korisnik prestane tipkati na određeno vrijeme.
import { useActionState } from 'react';
import { useState, useEffect } from 'react';
async function searchItems(prevState, query) {
// Simulate asynchronous search.
await new Promise(resolve => setTimeout(resolve, 500));
return `Search results for: ${query}`;
}
function MyComponent() {
const [query, setQuery] = useState('');
const [state, dispatch] = useActionState(searchItems, 'Initial State');
useEffect(() => {
const timeoutId = setTimeout(() => {
if (query) {
dispatch(query);
}
}, 300); // Debounce for 300ms
return () => clearTimeout(timeoutId);
}, [query, dispatch]);
return (
setQuery(e.target.value)}
/>
State: {state}
);
}
Najbolje prakse za useActionState
- Održavajte akcije čistima: Osigurajte da su vaše akcije čiste funkcije (ili što bliže tome). Ne bi trebale imati nuspojave osim ažuriranja stanja.
- Elegantno rukujte greškama: Uvijek rukujte greškama u svojim akcijama i pružite korisniku informativne poruke o greškama. Kao što je gore navedeno s poslužiteljskim akcijama, preferirajte vraćanje niza poruke o grešci iz poslužiteljske akcije umjesto bacanja greške.
- Optimizirajte performanse: Budite svjesni implikacija na performanse vaših akcija, posebno kada radite s velikim skupovima podataka. Razmislite o korištenju tehnika memoizacije kako biste izbjegli nepotrebna ponovna iscrtavanja.
- Uzmite u obzir pristupačnost: Osigurajte da vaša aplikacija ostane dostupna svim korisnicima, uključujući one s invaliditetom. Pružite odgovarajuće ARIA atribute i navigaciju tipkovnicom.
- Temeljito testiranje: Pišite jedinične testove i integracijske testove kako biste osigurali da vaše akcije i ažuriranja stanja rade ispravno.
- Internacionalizacija (i18n): Za globalne aplikacije, implementirajte i18n kako biste podržali više jezika i kultura.
- Lokalizacija (l10n): Prilagodite svoju aplikaciju određenim lokalitetima pružanjem lokaliziranog sadržaja, formata datuma i simbola valuta.
useActionState naspram drugih rješenja za upravljanje stanjem
Iako useActionState pruža prikladan način za upravljanje ažuriranjima stanja temeljenim na akcijama, nije zamjena za sva rješenja za upravljanje stanjem. Za složene aplikacije s globalnim stanjem koje se treba dijeliti između više komponenata, biblioteke poput Reduxa, Zustanda ili Jotaija mogle bi biti prikladnije.
Kada koristiti useActionState:
- Ažuriranja stanja jednostavne do umjerene složenosti.
- Ažuriranja stanja usko povezana s asinkronim akcijama.
- Integracija s React Server Components i poslužiteljskim akcijama.
Kada razmotriti druga rješenja:
- Složeno upravljanje globalnim stanjem.
- Stanje koje se treba dijeliti između velikog broja komponenata.
- Napredne značajke poput "time-travel" debuggiranja ili middlewarea.
Zaključak
Reactov useActionState hook nudi moćan i elegantan način za upravljanje ažuriranjima stanja pokrenutih asinkronim akcijama. Objedinjavanjem stanja učitavanja i grešaka, pojednostavljuje kod i poboljšava čitljivost, posebno pri radu s React Server Components i poslužiteljskim akcijama. Razumijevanje njegovih prednosti i ograničenja omogućuje vam da odaberete pravi pristup upravljanju stanjem za vašu aplikaciju, što dovodi do održivijeg i učinkovitijeg koda.
Slijedeći najbolje prakse navedene u ovom vodiču, možete učinkovito iskoristiti useActionState za poboljšanje korisničkog iskustva i razvojnog procesa vaše aplikacije. Ne zaboravite uzeti u obzir složenost vaše aplikacije i odabrati rješenje za upravljanje stanjem koje najbolje odgovara vašim potrebama. Od jednostavnih slanja obrazaca do složenih mutacija podataka, useActionState može biti vrijedan alat u vašem React razvojnom arsenalu.